
#include <main.h>
#include <math.h>

// MCP23017 I²C Address
#define MCP23017_ADDRESS  0x40  // Adjust based on A0, A1, A2 pin settings

// MCP23017 Register Addresses
#define IODIRA            0x00  // I/O direction register for GPIOA
#define IODIRB            0x01  // I/O direction register for GPIOB
#define GPIOA             0x12  // GPIO register for GPIOA
#define GPIOB             0x13  // GPIO register for GPIOB
#define OLATA             0x14  // Output latch register for GPIOA
#define OLATB             0x15  // Output latch register for GPIOB
#define EMERGENCY_BUTTON PIN_B0
#define LED_PIN     PIN_B7
#define LED_STATUS	PIN_D4


#define TIMEOUT_LIMIT 100  // 10 seconds timeout for RS232 communication


#define SENSOR_TA PIN_A1  // ADC input pin
#define ADC_CHANNEL 1      // ADC channel for A1

// Timer1 overflow counter
volatile int16 timer1_overflows = 0;


#define SCK_PIN PIN_B4
#define MISO_PIN PIN_B5
#define CS_PIN PIN_A5

// Function Prototypes
void mcp23017_init();
void mcp23017_writeA(BYTE data);
void mcp23017_writeB(BYTE data);
BYTE mcp23017_readA();
BYTE mcp23017_readB();
void rs232_search();
void max6675_init();
float read_max6675();
// Function Prototypes

int getMaxValue();
void process_adc_data();
#define VREF 5.0  // Reference voltage in volts
#define ADC_RESOLUTION 255  // 10-bit ADC

float getVoltage(int adc_value) {
    return (adc_value * VREF) / ADC_RESOLUTION;
}
volatile int emergency_pressed = 0; // Use 0 for false, 1 for true
long int timeout_counter = 0;  // Counter for communication timeout

#INT_EXT
void EXT_isr() {
    disable_interrupts(INT_EXT);  // Disable external interrupt to prevent re-entry

    // Debounce logic to ensure stable button detection
    delay_ms(50);  // Initial debounce delay
    if (input(EMERGENCY_BUTTON) == 0) {  // Check if the button is pressed (low state)
        delay_ms(50);  // Additional debounce delay for confirmation
        if (input(EMERGENCY_BUTTON) == 0) {  // Confirm button press
            emergency_pressed = true;  // Set emergency flag
            mcp23017_writeA(0x00);     // Turn off all GPIOA outputs
            mcp23017_writeB(0x00);     // Turn off all GPIOB outputs
            output_high(LED_PIN);      // Turn on emergency LED to indicate emergency mode

            // Blink the status LED during emergency
            while (emergency_pressed) {
	       printf("Emergency \n");
                output_toggle(LED_PIN);  // Toggle LED to indicate emergency state
                delay_ms(200);           // Blink delay
		

                // Check if the button is released
                if (input(EMERGENCY_BUTTON)) {
                    delay_ms(50);  // Debounce delay
                    if (input(EMERGENCY_BUTTON)) {  // Confirm button release
                        emergency_pressed = false;  // Clear emergency flag
                        output_low(LED_PIN);        // Turn off emergency LED
                        printf("Emergency resolved\n");
                    }
                }
            }
        }
    }

    enable_interrupts(INT_EXT);  // Re-enable the external interrupt
}
#INT_RTCC
void  RTCC_isr(void) 
{
   timeout_counter++;
}
// Timer1 ISR (increments overflow counter)
#INT_TIMER1
void TIMER1_isr() {
    timer1_overflows++;  // Count Timer1 overflows
}

// Function to get the max ADC value over 1 second
int getMaxValue() {
    int sensorValue;
    int sensorMax = 0;

    timer1_overflows = 0;  // Reset overflow counter
    set_timer1(0);         // Reset Timer1

    while (timer1_overflows < 16) {  // ~1 second with prescaler 1:8
        set_adc_channel(ADC_CHANNEL);
        delay_us(20);  // Allow ADC stabilization
        sensorValue = read_adc();
        
        if (sensorValue > sensorMax) {
            sensorMax = sensorValue;
        }
    }
    return sensorMax;
}


void process_adc_data() {
    int sensor_max = getMaxValue();  // Get peak ADC value

    // Define ADC Resolution (Change to 1023 for 10-bit ADC)
    #define ADC_MAX 255  // Assuming a 10-bit ADC (0-1023). Use 255 if 8-bit.

    // Sensor voltage range (SCT-013-000 outputs 0-1V)
    float sensor_voltage = (float)sensor_max / ADC_MAX * VREF;  // Convert ADC to voltage

    // SCT-013-000 has a max output of 1V for 100A
    #define SENSOR_MAX_VOLTAGE 1.0
    #define SENSOR_MAX_CURRENT 100.0

    // Convert voltage to current (linear scaling)
    float amplitude_current = (sensor_voltage / SENSOR_MAX_VOLTAGE) * SENSOR_MAX_CURRENT;

    // Compute RMS current
    float effective_value = amplitude_current / sqrt(2.0);
    disable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
    printf("Current RMS: %.2f A\n", effective_value);
    enable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
}
void setup_timer() {   
   enable_interrupts(INT_RTCC);
   enable_interrupts(INT_TIMER1);            // Enable Timer1 interrupt
   enable_interrupts(GLOBAL);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1|RTCC_8_BIT);		//64.0 us overflow
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); // Configure Timer1 (prescaler 1:8)   
   
}
void reset_timeout_counter() {
    timeout_counter = 0;  // Reset the timeout counter when acknowledgment is received
}

char message1[] = " 00.0 C";
signed int16 Celsius;



void main() {
   // ADC initialization
   setup_adc_ports(sAN0, VSS_VDD);
   setup_adc_ports(sAN1, VSS_VDD);   // Enable A1 for ADC
   setup_adc(ADC_CLOCK_INTERNAL | ADC_TAD_MUL_0);
   setup_adc(ADC_CLOCK_INTERNAL | ADC_TAD_MUL_0);   
   delay_ms(50);      //allow oscillator to stabilise 
   //Interrupt initialization
   set_tris_b(0x01); // Set RB0 as input (assuming it is INT_EXT pin)
   enable_interrupts(INT_EXT);  // Enable external interrupt
   ext_int_edge(H_TO_L);        // Trigger interrupt on falling edge
   setup_timer();     // Initialize Timer0 for communication timeout
   
   output_low(PIN_D6);

   // Configure the pins
   set_tris_b(0x30); // Set B4 (SCK) and B5 (MISO) as output and input
   set_tris_a(0x20); // Set A5 (CS) as output
   output_low(CS_PIN);  // Set CS high initially
   output_low(SCK_PIN);  // Initialize clock to low
   set_tris_c(0xBF);  // Set C6 as output (TX), C7 as input (RX)

   mcp23017_init();   // Initialize MCP23017
   max6675_init();    // Initialize MAX6675
   
   //Testing system rs232
   disable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
   printf("System Initialized\\n");
   enable_interrupts(INT_RTCC);   // Re-enable Timer0 interrupt
     
   while (TRUE) {
      disable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
      process_adc_data();  // Read and process ADC data
      enable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
      emergency_pressed = 0;  // Initialize emergency_pressed to 0 (no emergency state)
      // ADC read with delay
      set_adc_channel(0); // Select channel
      delay_us(20);       // Wait for ADC stabilization
      int16 adc_value = read_adc(); // Read ADC value
      
      //enable_interrupts(INT_EXT);
      if (emergency_pressed) {
	    // Handle the emergency state
	    printf("Emergency button pressed! Waiting for release...\n");
      }

      //look for characters sent by rs232
      rs232_search();
	
	
      //Communication timeout check
      if (timeout_counter > TIMEOUT_LIMIT) {	   
	    printf("Communication Timeout!\\n"); 
            mcp23017_writeA(0x00);
            mcp23017_writeB(0x00);
	    //Acknowladgment from App counter
            while (timeout_counter) {		//Stay in loop until acknowladgment received
		  disable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
		  delay_ms(100);
		  output_high(LED_STATUS);	//blink status led
		  delay_ms(500);
		  rs232_search(); 
		  output_low(LED_STATUS);	//blink status led
		  enable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
            }
	}
	
	 // Read temperature from MAX6675
        float temperature = read_max6675();
        if (temperature != -1.0) {
	    disable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
            printf("Temperature: %.2f°C\n", temperature);
	    enable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
        } else {
	    disable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
            printf("Error: Unable to read temperature\n");
	    enable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
        }
        
	//Read LM35
	 Celsius = read_adc() * 0.489;                 // Read analog voltage and convert it to Kelvin (0.489 = 500/1023)
	 if (Celsius > 99)
	    message1[0]  = 1 + 48;                  // Put 1 (of hundred)
	 else
	 message1[0]  = ' ';                     // Put space
	 message1[1]  = (Celsius / 10) % 10  + 48;
	 message1[2]  =  Celsius % 10  + 48;
	 message1[5] = 223;                        // Degree symbol   
	 // Print the Celsius and Kelvin temperatures
	 disable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
	 printf("Temperature (Celsius): %s\n", message1);
	 enable_interrupts(INT_RTCC);  // Disable Timer0 interrupt

	 disable_interrupts(INT_RTCC);  // Disable Timer0 interrupt
	 delay_ms(1000);  // Delay between readings (1 second)
	 enable_interrupts(INT_RTCC);  // Disable Timer0 interrupt	
	
    }
}





//Function to initialize max6675
void max6675_init() {
    output_high(CS_PIN);  // Set CS high initially
    delay_ms(1);
}
//Function to read from expander
float read_max6675() {
    disable_interrupts(GLOBAL);
    unsigned int16 raw_data = 0;
    float temperature = 0.0;
    float ambient_temperature = 25.0; // Assuming ambient temperature is 25°C, can be measured if required.
    output_low(CS_PIN);  // Select MAX6675
    delay_us(10);

    // Read 16 bits of data from MAX6675
    
    for (int bit = 0; bit < 16; bit++) {
        output_high(SCK_PIN);  // Clock high
        delay_us(1);
        raw_data = (raw_data << 1) | input(MISO_PIN);  // Shift left and read new bit
        output_low(SCK_PIN);  // Clock low
        delay_us(1);
    }
    

    output_high(CS_PIN);  // Deselect MAX6675
   
    // Check for thermocouple connectivity (Fault bit is at position 2)
    if (raw_data & 0x4) {  // Fault bit is set
        printf("Thermocouple not connected\n");
        return -1.0;  // Return error code
    }
    // If raw_data is 4688 or another specific error value, treat it as a disconnected thermocouple
    if (raw_data == 4688) {
        printf("Thermocouple not connected (raw data: 4688)\n");
        return -1.0;  // Return error code for disconnected thermocouple
    }
    raw_data >>= 4;  // Ignore lower 3 bits to get the actual temperature reading
    // The MAX6675 provides a value in 0.25°C steps, so each bit represents 0.25°C
    temperature = raw_data * 0.50 - 24;  // Convert to Celsius based on the MAX6675 datasheet
    // Compensation for cold-junction temperature (ambient temperature)
    float voltage_output = (temperature - ambient_temperature) * 41.0;  // VOUT = (41 µV / °C) * (TR - TAMB)
    printf("Temperature: %.2f°C\n", temperature);
    enable_interrupts(GLOBAL);
    return temperature;
    
}


//Function to search for rs232 
void rs232_search() {
    static BYTE output_state_A = 0; // Keeps track of the current state of GPIOA

    if (kbhit()) {
        char received_char = getc();  // Read the received character

        // Debug message for received character
        disable_interrupts(INT_RTCC);  // Temporarily disable Timer0 interrupt
        printf("Received: %c\n", received_char);
        enable_interrupts(INT_RTCC);  // Re-enable Timer0 interrupt

        if (received_char == 'A') {  // Acknowledgment signal
            reset_timeout_counter();  // Reset the timeout counter
        } else if (received_char >= '1' && received_char <= '7') {
            int output_num = received_char - '0';  // Convert ASCII to integer (1-7)

            // Set GPA0 and the corresponding pin based on the received number
            output_state_A = (1 << 0) | (1 << output_num);  // GPA0 + corresponding pin

            // Write the updated state to MCP23017
            mcp23017_writeA(output_state_A);

            // Debug message for output activation
            disable_interrupts(INT_RTCC);
            printf("GPA0 and GPA%d activated\n", output_num);
            enable_interrupts(INT_RTCC);

        } else if (received_char == '0') {  // Command to turn all outputs off
            output_state_A = 0; // Reset all GPIOA outputs

            // Write the reset state to MCP23017
            mcp23017_writeA(output_state_A);

            // Debug message for turning off all outputs
            disable_interrupts(INT_RTCC);
            printf("All outputs turned off\n");
            enable_interrupts(INT_RTCC);
        }
    }
}
// Function to initialize MCP23017
void mcp23017_init() {
    // Configure GPIOA as outputs

    i2c_start();
    i2c_write(MCP23017_ADDRESS);     // Send MCP23017 address with write bit
    i2c_write(IODIRA);               // Access IODIRA register
    i2c_write(0x00);                 // Set all GPIOA pins as outputs
    i2c_stop();

    // Configure GPIOB as inputs
    i2c_start();
    i2c_write(MCP23017_ADDRESS);     // Send MCP23017 address with write bit
    i2c_write(IODIRB);               // Access IODIRB register
    i2c_write(0xFF);                 // Set all GPIOB pins as inputs
    i2c_stop();
  
   
}

// Function to write data to GPIOA
void mcp23017_writeA(BYTE data) {

    i2c_start();
    i2c_write(MCP23017_ADDRESS);     // Send MCP23017 address with write bit
    i2c_write(GPIOA);                // Access GPIOA register
    i2c_write(data);                 // Write data to GPIOA
    i2c_stop();

}

// Function to write data to GPIOB
void mcp23017_writeB(BYTE data) {
    i2c_start();
    i2c_write(MCP23017_ADDRESS);     // Send MCP23017 address with write bit
    i2c_write(GPIOB);                // Access GPIOB register
    i2c_write(data);                 // Write data to GPIOB
    i2c_stop();
}

// Function to read data from GPIOA
BYTE mcp23017_readA() {
    BYTE data;
    i2c_start();
    i2c_write(MCP23017_ADDRESS);     // Send MCP23017 address with write bit
    i2c_write(GPIOA);                // Access GPIOA register
    i2c_start();                     // Restart communication for reading
    i2c_write(MCP23017_ADDRESS | 1); // Send MCP23017 address with read bit
    data = i2c_read(0);              // Read data from GPIOA (send NACK)
    i2c_stop();
    return data;
}

// Function to read data from GPIOB
BYTE mcp23017_readB() {
    BYTE data;
    i2c_start();
    i2c_write(MCP23017_ADDRESS);     // Send MCP23017 address with write bit
    i2c_write(GPIOB);                // Access GPIOB register
    i2c_start();                     // Restart communication for reading
    i2c_write(MCP23017_ADDRESS | 1); // Send MCP23017 address with read bit
    data = i2c_read(0);              // Read data from GPIOB (send NACK)
    i2c_stop();
    return data;
}